#ifndef HASH_H
#define HASH_H

#include "glm/glm.hpp"
#include "prime.h"

/* 3 very large primes used for hash function */
#define PRIME_1 73856093
#define PRIME_2 19349663
#define PRIME_3 83492791


typedef struct p {
    p() : density(998.29), mass(0.02), buoyancy(0), viscosity(6.5),
          surface_tension(0.1428), threshold(7.065), stiffness(30.0), 
          restitution(0.5), kernel(20), radius(0.0457), render(0.01685),
          time_step(0.005){}

    float density;          //density at rest 
    float mass;             //mass per particle (kg)
    float buoyancy;         //for gasses, gives them lift in air
    float viscosity;        //the viscosity constant
    float surface_tension;  //surface tension constant
    float threshold;        //threshold for whether a particle's surface normal 
                            // is fit to affect the forces.
    float stiffness;        //replaces rightside of pV = nRT for liquids
    float restitution;      //amount of kinetic energy conserved after gaseous
                            // particles colide. 0 <= restitution <= 1
    float kernel;           //avg# of particles in the support radius
    float radius;           //radius for the smoothing function
    float render;           //radius for rendering
    float count;
    float colorx, colory, colorz;
    float time_step;
} particle_attributes;

/* only using x, y, z for now.
 * the positions of the particles
 */
typedef struct Part {
    glm::vec3 pos, pos_prev, v, v_half, a;
    glm::vec3 color;
    /* internal forces, pressure and viscosity */
    glm::vec3 f_pressure, f_viscosity;
    /* external forces, gravity and surface tension */
    glm::vec3 f_gravity, f_surface;
    /* inward pointing normal */
    glm::vec3 normal;
    /* pressure of this particle */
    float pressure;
    float density, mass;
    int id;

    Part() {
        color = glm::vec3(0.f, 0.78f, 0.9f);
        density = pressure = mass = 0;
        a = v = v_half = f_pressure = f_viscosity = f_gravity = 
         f_surface = pos = pos_prev = glm::vec3(0.f);
        id = 0;
    }
} Particle;

typedef struct L_Node {
    L_Node *next;
    Particle *p;
    L_Node() {
        next = NULL;
        p = NULL;
    }

    ~L_Node() {
        if (next)
            delete next;
    }
} Node;

void print_particle(Particle *p);
void print_list(Node *n);

typedef struct HashTable {
    Node *nodes;
    int size;
    float dist;

    void insert(Particle *p);
    void remove(Particle *p);

    Node *lookUp(float x, float y, float z);
    Node *lookUpId(float x, float y, float z, int id);
    Node *cull(Node *list, glm::vec3 v, int id);

    int hash(glm::vec3 v);

    HashTable(int n, float h) {
        size = next_prime(2 * n);
        nodes = (Node *) calloc(sizeof(Node), size);

        dist = h;
    }

    ~HashTable() {
        int i;
        
        for (i = 0; i < size; i++) {
            delete nodes[i].next;
        }
        free(nodes);
    }
} HashTable;

#endif
